#include "heroz_graphics.hh"
#include <glob.h>
#include <iostream>
#include <stdexcept>
#include <string>
#include <SDL/SDL_image.h>
#include "fmemory.h"

using namespace std;

/* \brief extract an animation tile from the surface
 *
 * Extract an animation tile from a surface with multiple animation
 * sequences separated by a single pixel border.
 * Colour with index 0 is set to tranparent.
 *
 * \param surf A surface with multiple animations.
 * \param width Width of a single tile.
 * \param height Height of a single tile.
 * \param tile_x Which tile in x direction.
 * \param tile_y Which tile in y direction.
 */
SDL_Surface *extract_animation_frame(SDL_Surface *surf, int width, int height, int tile_x, int tile_y) {
  SDL_Rect src;
  SDL_Surface *surf2;
  SDL_Palette *palette;

  surf2 = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 8, 0, 0, 0, 0);
  if(surf2) {
    palette = surf->format->palette;
    SDL_SetPalette(surf2, SDL_LOGPAL, palette->colors, 0, palette->ncolors);
    src.x = 1 + (width + 1) * tile_x;
    src.y = 1 + (height + 1) * tile_y;
    src.w = width;
    src.h = height;
    SDL_BlitSurface(surf, &src, surf2, NULL);
    SDL_SetColorKey(surf2, SDL_SRCCOLORKEY, 0); //Use first color
  }
  return surf2;
}

int extract_width_height(SDL_Surface *surf, int &width, int &height) {
  if(surf->format->BitsPerPixel > 8) {
    throw std::invalid_argument("only 8 bpp allowed for sprite");
  }
  Uint8 *pixels = static_cast<Uint8*>(surf->pixels);
  for(width = 1; width < surf->w; ++width) {
    if(pixels[width] == 0) break;
  }
  for(height = 1; height < surf->h; ++height) {
    if(pixels[height * surf->pitch] == 0) break;
  }
  //Reduce by one as we have the border left and at the top.
  int numpicts = (surf->w / --width) * (surf->h / --height);
  return numpicts;
}

Sprite extract_animations(SDL_Surface *display, SDL_Surface *surf, int width, int height) {
  int animation, frame;
  SDL_Surface *surf2;
  Sprite spriteobj;
  sge_sprite *sprite;

  for(animation = 0; animation < surf->h / height; ++animation) {
    surf2 = extract_animation_frame(surf, width, height, 0, animation);
    if(!surf2) throw std::runtime_error("can not extract animation");
    /*
     * Why new? Pushing a sge_sprite into a container creates a
     * copy from the one below. Somehow copy-constructing
     * fails. Or whatever. I always get segfaults.
     */
    sprite = new sge_sprite(display, surf2);
    if(!sprite) throw std::runtime_error("new sge_sprite failed");
    for(frame = 1; frame < surf->w / width; ++frame) {
      surf2 = extract_animation_frame(surf, width, height, frame, animation);
      cout << "Adding frame " << frame << endl;
      if(!surf2) throw std::runtime_error("can not extract animation");
      sprite->add_frame(surf2);
    }
    sprite->set_fps(8);
    sprite->border_bounce(false);
    spriteobj.sprite_animations.push_back(sprite);
  }
  return spriteobj;
}

HerozGraphics::HerozGraphics(SDL_Surface *display_, const char *path) : display(display_) {
  int i;
  int width, height;
  glob_t globbuf;
  char **cptr;
  SDL_Surface *surf;
  string globname(path);

  globname += "/sprite.*";
  if((i = glob(globname.c_str(), 0, NULL, &globbuf)) != 0) {
    throw std::runtime_error(strerror(errno));
  }
  for(cptr = globbuf.gl_pathv; *cptr; ++cptr) {
    surf = IMG_Load(*cptr);
#ifdef DEBUG
    printf("loading '%s': %p\n", *cptr, surf);
#endif
    if(!surf) {
      throw runtime_error(SDL_GetError());
    } else {
      i = extract_width_height(surf, width, height);
      printf("sprite width=%d height=%d total_animations=%d\n", width, height, i);
      sprites.push_back(extract_animations(display, surf, width, height));
    }
  }
  globfree(&globbuf);
}

HerozGraphics::~HerozGraphics() {
}

void HerozGraphics::draw_sprites() {
  unsigned int i;

  for(i = 0; i < sprites.size(); ++i) {
    Sprite &sprite = sprites.at(i);
    if(sprite.current_animation >= 0) {
      sprite.anim()->update();
      sprite.anim()->draw();
    }
  }
}

void Sprite::change_anim(int i) {
  if(i != current_animation) {
    if(i < 0) i = -1;
    if(i >= 0 && current_animation >= 0) {
      sge_sprite *sprite = sprite_animations.at(i);
      sge_sprite *oldsprite = sprite_animations[current_animation];
      int sprtx = oldsprite->get_xpos();
      int sprty = oldsprite->get_ypos();
      sprite->move_to(sprtx, sprty);
      sprite->set_xpps(oldsprite->get_xpps());
      sprite->set_ypps(oldsprite->get_ypps());
      sprite->set_fps(oldsprite->get_fps());
    }
    current_animation = i;
  }
}
